package com.yanyu.space.sys.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yanyu.space.common.core.bean.db.PageEntity;
import com.yanyu.space.common.db.pagination.PageUtil;
import com.yanyu.space.common.core.exception.DataNotFoundException;
import com.yanyu.space.common.core.exception.ServiceException;
import com.yanyu.space.sys.bean.po.*;
import com.yanyu.space.sys.bean.vo.role.RoleAddVo;
import com.yanyu.space.sys.bean.vo.role.RoleSearchVo;
import com.yanyu.space.sys.bean.vo.role.RoleUpdateVo;
import com.yanyu.space.sys.dao.RoleDeptMapper;
import com.yanyu.space.sys.dao.RoleMapper;
import com.yanyu.space.sys.dao.RoleMenuMapper;
import com.yanyu.space.sys.dao.RoleUserMapper;
import com.yanyu.space.sys.bean.enums.SysRedisKeyEnum;
import com.yanyu.space.sys.bean.enums.SysRCode;
import com.yanyu.space.sys.service.IRoleService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 系统角色 业务实现类
 *
 * @author yanyu
 */
@Service
@Slf4j
@AllArgsConstructor
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {

    private RoleMenuMapper roleMenuMapper;
    private RoleDeptMapper roleDeptMapper;
    private RoleUserMapper roleUserMapper;
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public PageEntity<Role> listPage(RoleSearchVo vo) {
        IPage<Role> page = baseMapper.selectPage(PageUtil.getParam(),
                Wrappers.<Role>lambdaQuery().orderByAsc(Role::getSort).orderByDesc(Role::getUpdateTime));
        return PageUtil.getResult(page);
    }

    @Override
    public Role getById(Long id) {
        Role role = baseMapper.selectById(id);
        if (role == null) {
            throw new DataNotFoundException();
        }
        return role;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(RoleAddVo vo) {
        // 编码是否重复
        checkRoleCodeUnique(null, vo.getCode());

        // 新增角色
        Role role = BeanUtil.copyProperties(vo, Role.class);
        baseMapper.insert(role);

        // 菜单权限
        if (CollectionUtils.isNotEmpty(vo.getMenuIds())) {
            for (Long menuId : vo.getMenuIds()) {
                RoleMenu roleMenu = new RoleMenu();
                roleMenu.setRoleId(role.getId());
                roleMenu.setMenuId(menuId);
                roleMenuMapper.insert(roleMenu);
            }
        }
    }

    @Override
    public void update(RoleUpdateVo vo) {
        // 编码是否重复
        checkRoleCodeUnique(vo.getId(), vo.getCode());

        // 更新角色
        Role role = BeanUtil.copyProperties(vo, Role.class);
        baseMapper.updateById(role);

        // 菜单权限
        roleMenuMapper.delete(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId, vo.getId()));
        if (CollectionUtils.isNotEmpty(vo.getMenuIds())) {
            for (Long menuId : vo.getMenuIds()) {
                RoleMenu roleMenu = new RoleMenu();
                roleMenu.setRoleId(role.getId());
                roleMenu.setMenuId(menuId);
                roleMenuMapper.insert(roleMenu);
            }
        }
    }

    @Override
    public void updateDataScope(RoleUpdateVo vo) {
        // 更新角色
        Role role = new Role();
        role.setId(vo.getId());
        role.setDataScope(vo.getDataScope());
        role.setDeptCheckStrictly(vo.getDeptCheckStrictly());
        baseMapper.updateById(role);

        // 数据权限
        roleDeptMapper.delete(Wrappers.<RoleDept>lambdaQuery().eq(RoleDept::getRoleId, vo.getId()));
        if (CollectionUtils.isNotEmpty(vo.getDeptIds())) {
            for (Long deptId : vo.getDeptIds()) {
                RoleDept roleDept = new RoleDept();
                roleDept.setRoleId(role.getId());
                roleDept.setDeptId(deptId);
                roleDeptMapper.insert(roleDept);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(RoleUpdateVo vo) {
        Role role = new Role();
        role.setId(vo.getId());
        role.setEnabled(vo.getEnabled());
        baseMapper.updateById(role);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> ids) {
        // 是否已经绑定用户
        for (Long id : ids) {
            Role role = getById(id);
            long roleUserCnt = roleUserMapper.selectCount(Wrappers.<RoleUser>lambdaQuery().eq(RoleUser::getRoleId, id));
            if (roleUserCnt > 0) {
                throw new ServiceException(SysRCode.ROLE_ASSIGNED, role.getName());
            }
        }
        ids.stream().forEach(id -> {
            // 删除角色菜单关联关系
            roleMenuMapper.delete(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getRoleId, id));
            // 删除角色部门关联关系
            roleDeptMapper.delete(Wrappers.<RoleDept>lambdaQuery().eq(RoleDept::getRoleId, id));
        });
        baseMapper.deleteBatchIds(ids);
    }

    @Override
    public PageEntity<Role> listAllocated(Long userId, User user) {
        user.setId(userId);
        IPage<Role> page = baseMapper.listAllocated(PageUtil.getParam(), user);
        return PageUtil.getResult(page);
    }

    @Override
    public Set<String> listAllocated(Long userId) {
        String redisKey = SysRedisKeyEnum.ROLE_OF_USER.getCode(userId);
        boolean redisKeyFlag = stringRedisTemplate.hasKey(redisKey);
        if (redisKeyFlag) {
            Set<String> roleCodes = stringRedisTemplate.opsForSet().members(redisKey);
            log.info("从缓存中查询用户关联角色信息, 用户id：[{}], 关联角色信息：[{}]", userId, roleCodes.parallelStream().collect(Collectors.joining(",")));
            return roleCodes;
        }

        User user = new User();
        user.setId(userId);
        IPage<Role> page = baseMapper.listAllocated(PageUtil.getMaxParam(), user);
        List<Role> roles = page.getRecords();
        if (CollectionUtils.isNotEmpty(roles)) {
            Set<String> roleCodes = roles.parallelStream().map(Role::getCode).collect(Collectors.toSet());
            log.info("从数据库中获取用户关联角色信息, 用户id：[{}], 关联角色信息：[{}]", userId, roleCodes.parallelStream().collect(Collectors.joining(",")));
            stringRedisTemplate.opsForSet().add(SysRedisKeyEnum.ROLE_OF_USER.getCode(userId), roleCodes.toArray(new String[]{}));
            stringRedisTemplate.expire(SysRedisKeyEnum.ROLE_OF_USER.getCode(userId), SysRedisKeyEnum.ROLE_OF_USER.getTimeOut(1), SysRedisKeyEnum.ROLE_OF_USER.getUnit());
            return roleCodes;
        } else {
            log.info("当前用户没有关联角色信息, 用户id：[{}]", userId);
            return new HashSet<>();
        }
    }

    @Override
    public void delAllocatedUser(Long roleId, List<Long> userIds) {
        userIds.stream().forEach(userId -> {
            roleUserMapper.delete(Wrappers.<RoleUser>lambdaQuery().eq(RoleUser::getUserId, userId).eq(RoleUser::getRoleId, roleId));
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAllocatedUser(Long roleId, List<Long> userIds) {
        userIds.stream().forEach(userId -> {
            RoleUser roleUser = new RoleUser();
            roleUser.setRoleId(roleId);
            roleUser.setUserId(userId);
            roleUserMapper.insert(roleUser);
        });
    }

    /**
     * 判断角色编码是否存在
     *
     * @param id
     * @param code
     */
    private void checkRoleCodeUnique(Long id, String code) {
        Long codeCnt = baseMapper.selectCount(Wrappers.<Role>lambdaQuery()
                .ne(id != null, Role::getId, id)
                .eq(Role::getCode, code));
        if (codeCnt > 0) {
            throw new ServiceException(SysRCode.ROLE_CODE_EXIST);
        }
    }
}
